﻿//-------------------------------------
// WBFSSync - WBFSSync.exe
//
// Copyright 2009 Caian (ÔmΣga Frøst) <frost.omega@hotmail.com> :
//
// WBFSSync is Licensed under the terms of the Microsoft Reciprocal License (Ms-RL)
//
// ConfigurationReader.cs:
//
// Leitor de configurações
//
//-------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace WBFSSync
{
    class ConfigurationReader
    {
        //--------------------- Estruturas, Classes e Enumeradores úteis

        //--------------------- Constantes/Estáticas

        //--------------------- Variáveis

        Dictionary<String, String> ConfigurationParameters = new Dictionary<string, string>();
        String[] ConfigurationLines = null;

        //--------------------- Rotinas

        public Boolean GetBoolArg(String Name, Int32 order, out Boolean value)
        {
            Name = Name.ToLower();
            value = false;

            String val;
            if (ConfigurationParameters.TryGetValue(Name + '#' + order, out val))
            {
                bool parsed = Boolean.TryParse(val, out value);
                if (!parsed)
                {
                    int v;
                    parsed = Int32.TryParse(val, out v);
                    value = (v != 0);
                }

                return parsed;
            }

            return false;
        }

        public Boolean GetInt32Arg(String Name, Int32 order, out Int32 value)
        {
            Name = Name.ToLower();
            value = 0;

            String val;
            if (ConfigurationParameters.TryGetValue(Name + '#' + order, out val))
            {
                return Int32.TryParse(val, out value);
            }

            return false;
        }

        public Boolean GetInt64Arg(String Name, Int32 order, out Int64 value)
        {
            Name = Name.ToLower();
            value = 0;

            String val;
            if (ConfigurationParameters.TryGetValue(Name + '#' + order, out val))
            {
                return Int64.TryParse(val, out value);
            }

            return false;
        }

        public Boolean GetFloatArg(String Name, Int32 order, out float value)
        {
            Name = Name.ToLower();
            value = 0;

            String val;
            if (ConfigurationParameters.TryGetValue(Name + '#' + order, out val))
            {
                return float.TryParse(val, out value);
            }

            return false;
        }

        public Boolean GetStringArg(String Name, Int32 order, out String value)
        {
            Name = Name.ToLower();
            value = "";

            String val;
            if (ConfigurationParameters.TryGetValue(Name + '#' + order, out val))
            {
                int openstring = val.IndexOf('"');
                int closestring = val.LastIndexOf('"');

                if ((openstring >= 0) && (closestring > openstring))
                {
                    value = val.Substring(openstring + 1, closestring - openstring - 1);
                    return true;
                }
            }

            return false;
        }

        //--------------------------------------------------------------------------

        public void SetBoolArg(String Name, Int32 order, Boolean value)
        {
            Name = Name.ToLower();
            if (order >= 0)
            {
                if (ConfigurationParameters.ContainsKey(Name + '#' + order))
                {
                    ConfigurationParameters[Name + '#' + order] = (value ? (int)1 : (int)0).ToString();
                }
            }
            else
            {
                int k = 0;
                while (ConfigurationParameters.ContainsKey(Name + '#' + k)) k++;
                ConfigurationParameters.Add(Name + '#' + k, (value ? (int)1 : (int)0).ToString());
            }
        }

        public void SetInt32Arg(String Name, Int32 order, Int32 value)
        {
            Name = Name.ToLower();
            if (order >= 0)
            {
                if (ConfigurationParameters.ContainsKey(Name + '#' + order))
                {
                    ConfigurationParameters[Name + '#' + order] = value.ToString();
                }
            }
            else
            {
                int k = 0;
                while (ConfigurationParameters.ContainsKey(Name + '#' + k)) k++;
                ConfigurationParameters.Add(Name + '#' + k, value.ToString());
            }
        }

        public void SetInt64Arg(String Name, Int32 order, Int64 value)
        {
            Name = Name.ToLower();
            if (order >= 0)
            {
                if (ConfigurationParameters.ContainsKey(Name + '#' + order))
                {
                    ConfigurationParameters[Name + '#' + order] = value.ToString();
                }
            }
            else
            {
                int k = 0;
                while (ConfigurationParameters.ContainsKey(Name + '#' + k)) k++;
                ConfigurationParameters.Add(Name + '#' + k, value.ToString());
            }
        }

        public void SetFloatArg(String Name, Int32 order, float value)
        {
            Name = Name.ToLower();
            if (order >= 0)
            {
                if (ConfigurationParameters.ContainsKey(Name + '#' + order))
                {
                    ConfigurationParameters[Name + '#' + order] = value.ToString();
                }
            }
            else
            {
                int k = 0;
                while (ConfigurationParameters.ContainsKey(Name + '#' + k)) k++;
                ConfigurationParameters.Add(Name + '#' + k, value.ToString());
            }
        }

        public void SetStringArg(String Name, Int32 order, String value)
        {
            Name = Name.ToLower();
            if (order >= 0)
            {
                if (ConfigurationParameters.ContainsKey(Name + '#' + order))
                {
                    ConfigurationParameters[Name + '#' + order] = '"' + value + '"';
                }
            }
            else
            {
                int k = 0;
                while (ConfigurationParameters.ContainsKey(Name + '#' + k)) k++;
                ConfigurationParameters.Add(Name + '#' + k, '"' + value + '"');
            }
        }

        //--------------------------------------------------------------------------

        public bool GetParameterLine(String p, List<String> Lines, out int l)
        {
            l = -1;

            int sharppos = p.IndexOf('#');
            string pvar = p.Substring(0, sharppos).ToLower();
            int porder = Int32.Parse(p.Substring(sharppos + 1));

            int j = 0;
            for (int i = 0; i < Lines.Count; i++)
            {
                string line = Lines[i].TrimStart();

                int comment = line.IndexOf("//");
                if (comment >= 0) line = line.Remove(comment);

                if (line.Length == 0) continue;

                int equalsignpos = line.IndexOf('=');
                if (equalsignpos <= 0) continue;

                String var = line.Substring(0, equalsignpos).Trim().ToLower();
                if (var == pvar)
                { 
                    l = i;
                    if (j++ == porder)
                        return true;
                }
            }

            return false;
        }

        public void RemoveParameterFromConfiguration(String parameter)
        {
            parameter = parameter.ToLower();

            //Remove o parâmetro da lista
            
            int k = 0;
            while (ConfigurationParameters.ContainsKey(parameter + '#' + k))
            {
                ConfigurationParameters.Remove(parameter + '#' + k);
                k++;
            }

            //Remove o parâmetro do arquivo final

            List<String> NewConfigurationLines = new List<string>(ConfigurationLines);

            for (int i = NewConfigurationLines.Count - 1; i >= 0; i--)
            {
                string line = ConfigurationLines[i].TrimStart();

                int comment = line.IndexOf("//");
                if (comment >= 0) line = line.Remove(comment);

                if (line.Length == 0) continue;

                int equalsignpos = line.IndexOf('=');
                if (equalsignpos <= 0) continue;

                String var = line.Substring(0, equalsignpos).Trim().ToLower();
                if (var == parameter) 
                    NewConfigurationLines.RemoveAt(i);
            }

            ConfigurationLines = new String[NewConfigurationLines.Count];
            NewConfigurationLines.CopyTo(ConfigurationLines);
        }

        //--------------------------------------------------------------------------

        public void BuildEntryPointMap(List<String> Lines, ref Dictionary<String, int> map)
        {
            map.Clear();

            for (int i = 0; i < Lines.Count; i++)
            {
                string line = Lines[i].TrimStart();

                int comment = line.IndexOf("//");
                if ((comment == 0) && (line.Length > 14)) //14 primeiros carateres: //<ENTRYPOINT=
                {
                    if (!line.StartsWith("//<ENTRYPOINT=")) continue; //Não é EntryPoint
                    int closeentry = line.IndexOf('>');
                    if (closeentry < 0) continue; //EntryPoint inválido

                    string entrypoint = line.Substring(14, closeentry - 14).ToLower();
                    if (!map.ContainsKey(entrypoint)) map.Add(entrypoint, i);
                }
            }
        }

        //--------------------------------------------------------------------------

        public Boolean OpenConfigurationFile(String file)
        {
            try
            {
                if (!File.Exists(file)) return false;

                ConfigurationParameters.Clear();

                ConfigurationLines = File.ReadAllLines(file, Encoding.Default);
                for (int i = 0; i < ConfigurationLines.Length; i++)
                {
                    string line = ConfigurationLines[i].TrimStart();

                    int comment = line.IndexOf("//");
                    if (comment >= 0) line = line.Remove(comment);

                    if (line.Length == 0) continue;

                    int equalsignpos = line.IndexOf('=');
                    if (equalsignpos <= 0) continue;

                    String var = line.Substring(0, equalsignpos).Trim().ToLower();
                    String val = line.Substring(equalsignpos + 1).Trim();

                    //Salva a variável no formato var + k onde k é o número de ocorrências daquela variável

                    int k = 0;
                    while (ConfigurationParameters.ContainsKey(var + '#' + k)) k++;
                    ConfigurationParameters.Add(var + '#' + k, val);
                }

                return true;
            }
            catch (Exception e)
            {
                Log.LogException(e);
            }

            return false;
        }

        public Boolean UpdateConfigurationFile(String file)
        {
            try
            {
                if (!File.Exists(file)) return false;

                Dictionary<String, Int32> EntryPointMap = new Dictionary<string, int>();
                List<String> NewConfigurationLines = new List<string>(ConfigurationLines);
                
                BuildEntryPointMap(NewConfigurationLines, ref EntryPointMap);

                //Usa query da LINQ para ordenar o dicionário
                var items = from k in ConfigurationParameters.Keys
                            orderby k ascending
                            select k;

                foreach (String key in items)
                {
                    String val = ConfigurationParameters[key];
                    String var = key.Substring(0, key.IndexOf('#'));

                    //Verifica se o parâmetro já existe
                    int l;
                    if (GetParameterLine(key, NewConfigurationLines, out l))
                    {
                        string line = var + '=' + val;

                        //Verifica se o parâmetro tem comentário
                        int comment = NewConfigurationLines[l].IndexOf("//");
                        if (comment > 0) line += NewConfigurationLines[l].Substring(comment);

                        NewConfigurationLines[l] = line;
                    }
                    else
                    {
                        //Verifica se o parâmetro possui um antecessor
                        if (l >= 0)
                        {
                            NewConfigurationLines.Insert(l + 1, var + '=' + val);
                        }
                        else
                        {
                            //Verifica se o parâmetro possui um ENTRYPOINT
                            int e;
                            if (EntryPointMap.TryGetValue(var, out e))
                            {
                                //Remonta o EntryPoint
                                BuildEntryPointMap(NewConfigurationLines, ref EntryPointMap);

                                NewConfigurationLines.Insert(++e, var + '=' + val);
                            }
                            else
                            {
                                //Não possui EntryPoint, coloca o parâmetro no final do arquivo
                                NewConfigurationLines.Add(var + '=' + val);
                            }
                        }
                    }
                }

                File.WriteAllLines(file, NewConfigurationLines.ToArray(), Encoding.Default);

                return true;
            }
            catch (Exception e)
            {
                Log.LogException(e);
            }

            return false;
        }
    }
}